﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using Microsoft.SharePoint;
using System.Collections.ObjectModel;

namespace Actuant.Utility
{
    public static class SPListHelper
    {
        #region Public Methods

        /// <summary>
        /// Determines whether the specified list contains a field with the specified id.
        /// </summary>
        /// <param name="list">
        /// The list.
        /// </param>
        /// <param name="fieldId">
        /// The field id.
        /// </param>
        /// <returns>
        /// <c>true</c> if the specified field id contains field; otherwise, <c>false</c>.
        /// </returns>
        public static bool ContainsField(SPList list, Guid fieldId)
        {           
            // Not an ideal implementation unfortunately SharePoint does not provide an api
            // to check if a field with a specific Id is part of the list.
            // The other option would be to iterate through all the fields which is not ideal either.
            try
            {
                if (list.Fields[fieldId] != null)
                {
                    return true;
                }

                return false;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// Ensures that the espcified field exists on a list. 
        /// It throws a meaningfull exception if the field doesn't exist
        /// instead of the usual SP "Value does not fall withing expected range" exception)
        /// </summary>
        /// <param name="list">
        /// List
        /// </param>
        /// <param name="fieldName">
        /// Field Name
        /// </param>
        public static void EnsureFieldExists(SPList list, string fieldName)
        {
            if (list == null)
            {
                throw new ArgumentNullException("list");
            }

            if (!list.Fields.ContainsField(fieldName))
            {
                string errorMsg = String.Format(CultureInfo.CurrentCulture, "List {0} does not contain field named {1}", list.Title, fieldName);
                throw new ArgumentOutOfRangeException(errorMsg);
            }
        }

        /// <summary>
        /// Safelly gets the Field details of a SPList.
        /// </summary>
        /// <param name="list">
        /// List
        /// </param>
        /// <param name="fieldName">
        /// Filed Name
        /// </param>
        /// Field
        /// <returns>
        /// Return Field
        /// </returns>
        public static SPField GetField(SPList list, string fieldName)
        {
            if (list == null)
            {
                throw new ArgumentNullException("list");
            }

            if (String.IsNullOrEmpty(fieldName))
            {
                throw new ArgumentNullException("fieldName");
            }

            EnsureFieldExists(list, fieldName);

            return list.Fields[fieldName];
        }

        /// <summary>
        /// Gets the field with the specified Id.
        /// </summary>
        /// <param name="list">
        /// The list.
        /// </param>
        /// <param name="fieldId">
        /// The field id.
        /// </param>
        /// <returns>
        /// An instance of the field with the specified id.
        /// </returns>
        public static SPField GetField(SPList list, Guid fieldId)
        {
            try
            {
                return list.Fields[fieldId];
            }
            catch (Exception ex)
            {
                throw new ArgumentOutOfRangeException(string.Format(CultureInfo.CurrentCulture, "Cannot get field with ID '{0}' from list '{1}'", fieldId, list.Title), ex);
            }
        }

        /// <summary>
        /// Gets a list from an SPWeb. It throws a meaningfull error if the list does not exist.
        /// </summary>
        /// <param name="web">
        /// SPWeb containing the Lists collection.
        /// </param>
        /// <param name="listName">
        /// string Name of the list
        /// </param>
        /// <returns>
        /// true if it does exits; otherwise false.
        /// </returns>
        public static SPList GetList(SPWeb web, string listName)
        {          
            SPList list = TryGetList(web, listName);
            if (list == null)
            {
                throw new ArgumentException("Exception in trying to get list with name: '" + listName + "' from web '" + web.Title + "'");
            }

            return list;
        }

        /// <summary>
        /// Gets a list from an SPWeb. It throws a meaningfull error if the list does not exist.
        /// </summary>
        /// <param name="web">
        /// SPWeb containing the Lists collection.
        /// </param>
        /// <param name="value">
        /// The value.
        /// </param>
        /// <param name="parentContentTypeId">
        /// The parent Content Type Id.
        /// </param>
        /// <returns>
        /// List of content types.
        /// </returns>
        public static Collection<SPContentType> GetContentTypes(SPWeb web, Guid value, string parentContentTypeId)
        {

            SPList documentLibrary = web.Lists[value];
            SPContentTypeCollection spContentTypeCollection = documentLibrary.ContentTypes;
            Collection<SPContentType> spContentTypeCollectionFiltered = new Collection<SPContentType>();

            if (spContentTypeCollection == null)
            {
                throw new ArgumentException("Exception in trying to get content types for the list: '" + value + "' from web '" + web.Title + "'");
            }

            foreach (SPContentType filtered in spContentTypeCollection)
            {
                // Filter content types by base type id if needed
                if (!string.IsNullOrEmpty(parentContentTypeId))
                {
                    if (filtered.Id.ToString().Contains(parentContentTypeId))
                    {
                        spContentTypeCollectionFiltered.Add(filtered);
                    }
                }
                else
                {
                    // Otherwise just add Content Type to Collection
                    spContentTypeCollectionFiltered.Add(filtered);
                }
            }

            return spContentTypeCollectionFiltered;
        }

        /// <summary>
        /// Tries to get a list from an SPWeb. It returns null if the list does not exist.
        /// </summary>
        /// <param name="web">
        /// SPWeb containing the Lists collection.
        /// </param>
        /// <param name="listName">
        /// string Name of the list
        /// </param>
        /// <returns>
        /// SPList object or null if the list does not exist
        /// </returns>
        public static SPList TryGetList(SPWeb web, string listName)
        {
          
            try
            {
                SPList list = web.Lists[listName];
                return list;
            }
            catch (Exception)
            {
                return null;
            }
        }

        #endregion

        /*
        #region Public Methods

        /// <summary>
        /// Adds the specified <paramref name="file"/> to the root folder of the
        /// given <paramref name="list">list.</paramref>.
        /// </summary>
        /// <param name="file">
        /// The file to upload.
        /// </param>
        /// <param name="list">
        /// The destination list.
        /// </param>
        public static void AddFile(FileSystemInfo file, SPList list)
        {
            if (file == null)
            {
                throw new ArgumentNullException("file");
            }

            if (list == null)
            {
                throw new ArgumentNullException("list");
            }

            AddFileToList(file, list.RootFolder);
        }

        /// <summary>
        /// Adds the specified <paramref name="file"/> to the specified <paramref name="folder"/> of the
        /// given <paramref name="list">list.</paramref>.
        /// </summary>
        /// <param name="file">
        /// The file to upload.
        /// </param>
        /// <param name="list">
        /// The destination list.
        /// </param>
        /// <param name="folder">
        /// The folder to add the file to.
        /// </param>
        /// <param name="contentTypeName">
        /// Name of the content type for this document.
        /// </param>
        public static void AddFile(FileSystemInfo file, SPList list, SPFolder folder, string contentTypeName)
        {
            if (list == null)
            {
                throw new ArgumentNullException("list");
            }

            SPFile sharepointFile = AddFileToList(file, folder);
            sharepointFile.Item["ContentTypeId"] = list.ContentTypes[contentTypeName].Id;
            sharepointFile.Item.Update();
        }

        /// <summary>
        /// Ensures that the espcified field exists on a list. 
        /// It throws a meaningfull exception if the field doesn't exist
        /// instead of the usual SP "Value does not fall withing expected range" exception)
        /// </summary>
        /// <param name="list">
        /// List
        /// </param>
        /// <param name="fieldName">
        /// Field Name
        /// </param>
        public static void EnsureFieldExists(SPList list, string fieldName)
        {
            if (list == null)
            {
                throw new ArgumentNullException("list");
            }

            if (!list.Fields.ContainsField(fieldName))
            {
                string errorMsg = String.Format(
                    CultureInfo.CurrentCulture, "List {0} does not contain field named {1}", list.Title, fieldName);
                Console.WriteLine(errorMsg);
                throw new ArgumentOutOfRangeException(errorMsg);
            }
        }

        /// <summary>
        /// The ensure field exists.
        /// </summary>
        /// <param name="web">
        /// The web.
        /// </param>
        /// <param name="listName">
        /// The list name.
        /// </param>
        /// <param name="fieldName">
        /// The field name.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static void EnsureFieldExists(SPWeb web, string listName, string fieldName)
        {
            if (web == null)
            {
                throw new ArgumentNullException("web");
            }

            SPList list = GetList(web, listName);
            EnsureFieldExists(list, fieldName);
        }

        /// <summary>
        /// The ensure field exists.
        /// </summary>
        /// <param name="item">
        /// The item.
        /// </param>
        /// <param name="fieldName">
        /// The field name.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// </exception>
        public static void EnsureFieldExists(SPListItem item, string fieldName)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            if (!item.Fields.ContainsField(fieldName))
            {
                string errorMsg = String.Format(
                    CultureInfo.CurrentCulture, 
                    "List {0} does not contain field named {1}", 
                    item.ParentList.Title, 
                    fieldName);
                Console.WriteLine(errorMsg);
                throw new ArgumentOutOfRangeException(errorMsg);
            }
        }

        /// <summary>
        /// Check if a list exists (unfortunatelly the only way to do this is to try to get the list)
        /// </summary>
        /// <param name="web">
        /// Web
        /// </param>
        /// <param name="listName">
        /// List Name
        /// </param>
        public static void EnsureListExists(SPWeb web, string listName)
        {
            GetList(web, listName);
        }

        /// <summary>
        /// Safelly gets the double value of a SPListItem colum.
        /// </summary>
        /// <param name="item">
        /// Item
        /// </param>
        /// <param name="fieldName">
        /// Filed Name
        /// </param>
        /// <returns>
        /// The get column double value.
        /// </returns>
        public static double GetCalculatedColumnDoubleValue(SPListItem item, string fieldName)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            EnsureFieldExists(item, fieldName);

            object obj = item[fieldName];

            if (obj != null)
            {
                return Convert.ToDouble(
                    obj.ToString().ToUpperInvariant().Replace(@"float;#", String.Empty), CultureInfo.InvariantCulture);
            }

            return 0;
        }

        /// <summary>
        /// Safelly gets the int value of a SPListItem colum.
        /// </summary>
        /// <param name="item">
        /// Item
        /// </param>
        /// <param name="fieldName">
        /// Field Name
        /// </param>
        /// <returns>
        /// The get column bool value.
        /// </returns>
        public static bool GetColumnBoolValue(SPListItem item, string fieldName)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            EnsureFieldExists(item, fieldName);

            object obj = item[fieldName];

            return Convert.ToBoolean(obj, CultureInfo.InvariantCulture);
        }

        /// <summary>
        /// Safelly gets the datetime value of a SPListItem colum.
        /// </summary>
        /// <param name="item">
        /// Item
        /// </param>
        /// <param name="fieldName">
        /// Field Name 
        /// </param>
        /// <returns>
        /// The get column datetime value.
        /// </returns>
        public static DateTime GetColumnDateTimeValue(SPListItem item, string fieldName)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            EnsureFieldExists(item, fieldName);

            object obj = item[fieldName];

            if (obj != null)
            {
                return Convert.ToDateTime(obj.ToString(), CultureInfo.InvariantCulture);
            }

            return new DateTime(SPUtility.GetCalendarYearMin((int) SPCalendarType.Gregorian), 1, 1);
        }

        /// <summary>
        /// Safelly gets the double value of a SPListItem colum.
        /// </summary>
        /// <param name="item">
        /// Item
        /// </param>
        /// <param name="fieldName">
        /// Filed Name
        /// </param>
        /// <returns>
        /// The get column double value.
        /// </returns>
        public static double GetColumnDoubleValue(SPListItem item, string fieldName)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            EnsureFieldExists(item, fieldName);

            object obj = item[fieldName];

            if (obj != null)
            {
                return Convert.ToDouble(obj, CultureInfo.InvariantCulture);
            }

            return 0;
        }

        /// <summary>
        /// Safelly gets the int value of a SPListItem colum.
        /// </summary>
        /// <param name="item">
        /// Item
        /// </param>
        /// <param name="fieldName">
        /// Field Name
        /// </param>
        /// <returns>
        /// The get column int value.
        /// </returns>
        public static int GetColumnIntValue(SPListItem item, string fieldName)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            EnsureFieldExists(item, fieldName);

            object obj = item[fieldName];

            if (obj != null)
            {
                return Convert.ToInt32(obj, CultureInfo.InvariantCulture);
            }

            return 0;
        }

        /// <summary>
        /// Safelly gets the int value of a SPListItem colum.
        /// </summary>
        /// <param name="properties">
        /// Properties
        /// </param>
        /// <param name="fieldName">
        /// Field Name
        /// </param>
        /// <returns>
        /// The get column int value.
        /// </returns>
        public static int GetColumnIntValue(SPItemEventDataCollection properties, string fieldName)
        {
            // EnsureFieldExists(item, fieldName);
            if (properties == null)
            {
                throw new ArgumentNullException("properties");
            }

            object obj = properties[fieldName];

            if (obj != null)
            {
                return Convert.ToInt32(obj, CultureInfo.InvariantCulture);
            }

            return 0;
        }

        /// <summary>
        /// Safelly gets the int value of a SPListItem colum.
        /// </summary>
        /// <param name="item">
        /// Item
        /// </param>
        /// <param name="fieldName">
        /// Filed Name
        /// </param>
        /// <returns>
        /// The get column int value.
        /// </returns>
        public static long GetColumnLongValue(SPListItem item, string fieldName)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            EnsureFieldExists(item, fieldName);

            object obj = item[fieldName];

            if (obj != null)
            {
                return Convert.ToInt64(obj, CultureInfo.InvariantCulture);
            }

            return 0;
        }

        /// <summary>
        /// Safelly gets the string value of a SPListItem colum.
        /// </summary>
        /// <param name="item">
        /// Item
        /// </param>
        /// <param name="fieldName">
        /// Field Name
        /// </param>
        /// <returns>
        /// The get column string value.
        /// </returns>
        public static string GetColumnStringValue(SPListItem item, string fieldName)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            EnsureFieldExists(item, fieldName);

            object obj = item[fieldName];

            if (obj != null)
            {
                return obj.ToString();
            }

            return String.Empty;
        }

        /// <summary>
        /// Safelly gets the string value of a properties colum.
        /// </summary>
        /// <param name="properties">
        /// Properties
        /// </param>
        /// <param name="fieldName">
        /// Field Name
        /// </param>
        /// <returns>
        /// The get column string value.
        /// </returns>
        public static string GetColumnStringValue(SPItemEventDataCollection properties, string fieldName)
        {
            if (properties == null)
            {
                throw new ArgumentNullException("properties");
            }

            // EnsureFieldExists(item, fieldName);
            object obj = properties[fieldName];

            if (obj != null)
            {
                return obj.ToString();
            }

            return String.Empty;
        }

        /// <summary>
        /// The get column url value.
        /// </summary>
        /// <param name="item">
        /// The item.
        /// </param>
        /// <param name="fieldName">
        /// The field name.
        /// </param>
        /// <returns>
        /// URL Value
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public static SPFieldUrlValue GetColumnUrl(SPListItem item, string fieldName)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            EnsureFieldExists(item, fieldName);

            var obj = item[fieldName] as SPFieldUrlValue;

            if (obj != null)
            {
                return obj;
            }

            return null;
        }

        /// <summary>
        /// Safelly gets the object value of a SPListItem colum.
        /// </summary>
        /// <param name="item">
        /// Item
        /// </param>
        /// <param name="fieldName">
        /// Field Name
        /// </param>
        /// <returns>
        /// The get column value.
        /// </returns>
        public static object GetColumnValue(SPListItem item, string fieldName)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            EnsureFieldExists(item, fieldName);

            return item[fieldName];
        }

        /// <summary>
        /// Safelly gets the Field details of a SPList.
        /// </summary>
        /// <param name="list">
        /// List
        /// </param>
        /// <param name="fieldName">
        /// Filed Name
        /// </param>
        /// Field
        /// <returns>
        /// Return Field
        /// </returns>
        public static SPField GetField(SPList list, string fieldName)
        {
            if (list == null)
            {
                throw new ArgumentNullException("list");
            }

            if (String.IsNullOrEmpty(fieldName))
            {
                throw new ArgumentNullException("fieldName");
            }

            EnsureFieldExists(list, fieldName);

            return list.Fields[fieldName];
        }

        /// <summary>
        /// Gets a list from an SPWeb. It throws a meaningfull error if the list does not exist.
        /// </summary>
        /// <param name="web">
        /// SPWeb containing the Lists collection.
        /// </param>
        /// <param name="listName">
        /// string Name of the list
        /// </param>
        /// <returns>
        /// true if it does exits; otherwise false.
        /// </returns>
        public static SPList GetList(SPWeb web, string listName)
        {
            ValidationHelper.VerifyArgument(web, "web");
            ValidationHelper.VerifyArgument(listName, "listName");

            SPList list = TryGetList(web, listName);
            if (list == null)
            {
                throw new ArgumentException(
                    "Exception in trying to get list with name: '" + listName + "' from web '" + web.Title + "'");
            }

            return list;
        }

        /// <summary>
        /// Safelly gets the datetime value of a SPListItem colum.
        /// </summary>
        /// <param name="item">
        /// Item
        /// </param>
        /// <param name="fieldName">
        /// Field Name
        /// </param>
        /// <returns>
        /// The get column datetime value.
        /// </returns>
        public static DateTime? GetNullableColumnDateTimeValue(SPListItem item, string fieldName)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            EnsureFieldExists(item, fieldName);

            object obj = item[fieldName];

            if (obj != null)
            {
                return Convert.ToDateTime(obj.ToString(), CultureInfo.InvariantCulture);
            }

            return null;
        }

        /// <summary>
        /// Checks if a list item is approved
        /// </summary>
        /// <param name="item">
        /// Item
        /// </param>
        /// <returns>
        /// The item is approved.
        /// </returns>
        public static bool ItemIsApproved(SPListItem item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            if (item["_ModerationStatus"] != null && item["_ModerationStatus"].ToString() == "0")
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Safelly sets the value of a list item
        /// </summary>
        /// <param name="item">
        /// Item
        /// </param>
        /// <param name="fieldName">
        /// Field Name
        /// </param>
        /// <param name="value">
        /// Value
        /// </param>
        public static void SetColumnDateTimeValue(SPListItem item, string fieldName, object value)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            EnsureFieldExists(item, fieldName);

            item[fieldName] = value == null ? DBNull.Value : value;
        }

        /// <summary>
        /// Safelly sets the value of a list item
        /// </summary>
        /// <param name="item">
        /// Item
        /// </param>
        /// <param name="fieldName">
        /// Field Name
        /// </param>
        /// <param name="value">
        /// Value
        /// </param>
        public static void SetColumnUrlValue(SPListItem item, string fieldName, SPFieldUrlValue value)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            EnsureFieldExists(item, fieldName);

            item[fieldName] = value;
        }

        /// <summary>
        /// Safelly sets the value of a list item
        /// </summary>
        /// <param name="item">
        /// Item
        /// </param>
        /// <param name="fieldName">
        /// Field Name
        /// </param>
        /// <param name="value">
        /// Value
        /// </param>
        public static void SetColumnValue(SPListItem item, string fieldName, object value)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            EnsureFieldExists(item, fieldName);

            item[fieldName] = value;
        }

        /// <summary>
        /// Safelly gets the string value of a SPListItem colum.
        /// </summary>
        /// <param name="item">
        /// Item
        /// </param>
        /// <param name="fieldName">
        /// Field Name
        /// </param>
        /// <returns>
        /// The try get column string value.
        /// </returns>
        public static string TryGetColumnStringValue(SPListItem item, string fieldName)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            // If item does not contain field then return null
            if (!item.Fields.ContainsField(fieldName))
            {
                return String.Empty;
            }

            object obj = item[fieldName];

            if (obj != null)
            {
                return obj.ToString();
            }

            return String.Empty;
        }

        /// <summary>
        /// Tries to get the value of a SPListItem colum. Returns the default for the epscified type
        /// if field does not exist.
        /// </summary>
        /// <typeparam name="T">
        /// Type parameter
        /// </typeparam>
        /// <param name="item">
        /// Item
        /// </param>
        /// <param name="fieldName">
        /// Field Name
        /// </param>
        /// <returns>
        /// Type
        /// </returns>
        public static T TryGetColumnValue<T>(SPListItem item, string fieldName)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }

            // If item does not contain field then return null
            if (!item.Fields.ContainsField(fieldName))
            {
                return default(T);
            }

            object obj = item[fieldName];

            if (obj == null)
            {
                return default(T);
            }

            return (T) Convert.ChangeType(obj, typeof (T), CultureInfo.InvariantCulture);
        }

        /// <summary>
        /// Tries to get a list from an SPWeb. It returns null if the list does not exist.
        /// </summary>
        /// <param name="web">
        /// SPWeb containing the Lists collection.
        /// </param>
        /// <param name="listName">
        /// string Name of the list
        /// </param>
        /// <returns>
        /// SPList object or null if the list does not exist
        /// </returns>
        public static SPList TryGetList(SPWeb web, string listName)
        {
            ValidationHelper.VerifyArgument(web, "web");
            ValidationHelper.VerifyArgument(listName, "listName");

            try
            {
                SPList list = web.Lists[listName];
                return list;
            }
            catch (Exception)
            {
                return null;
            }
        }

        #endregion

        #region Helper Methods

        /// <summary>
        /// Adds a file to the specified <paramref name="folder"/>.
        /// </summary>
        /// <param name="file">
        /// The file to add.
        /// </param>
        /// <param name="folder">
        /// The folder to add the file to.
        /// </param>
        /// <returns>
        /// The newly added file.
        /// </returns>
        private static SPFile AddFileToList(FileSystemInfo file, SPFolder folder)
        {
            SPFile newFile = folder.Files.Add(file.Name, File.ReadAllBytes(file.FullName));
            newFile.Update();

            return newFile;
        }

        #endregion
       */
    }
}
